package nl.utwente.eemcs.common.analysis;

import java.util.ArrayList;
import java.util.List;

import nl.utwente.eemcs.graph.BranchedConstruct;
import nl.utwente.eemcs.graph.ConditionalConstruct;
import nl.utwente.eemcs.graph.Edge;
import nl.utwente.eemcs.graph.EdgeType;
import nl.utwente.eemcs.graph.Graph;
import nl.utwente.eemcs.graph.LoopConstruct;
import nl.utwente.eemcs.graph.Node;

/**
 * The Execution guarantee analyzer checks for each node if the execution of the node is guaranteeed.
 * In case of loopbranches or conditional branches, execution depends on the evaluation of a condition and therefore execution of the branches is not guaranteed.
 * This step is important for data analysis.
 * @author Evert Duipmans
 */
public class ExecutionGuaranteeAnalyzer {
	public static Graph analyze(Graph inputGraph) {
		markExecutionGuarantee(inputGraph.getStartNode(), inputGraph, true, new ArrayList<Node>());
		return inputGraph;
	}
	
	/**
	 * Identifies for each node if the execution of the node is guaranteed. Each node is marked with this value
	 * @param currentNode Current node
	 * @param graph Graph
	 * @param executionGuaranteed Execution is guaranteed
	 * @param visitedNodes The visited nodes
	 */
	private static void markExecutionGuarantee(Node currentNode, Graph graph, boolean executionGuaranteed, List<Node> visitedNodes) {
		if (visitedNodes.contains(currentNode))
			return;

		// Mark node
		currentNode.setExecutionGuaranteed(executionGuaranteed);
		visitedNodes.add(currentNode);
		
		// In case of branched constructs, conditional branches are not always executed, since it depends on the evaluated condition
		if (currentNode instanceof BranchedConstruct<?, ?>) {
			boolean newVal = (currentNode instanceof ConditionalConstruct) ? false : executionGuaranteed;
			
			BranchedConstruct<?, ?> castedNode = (BranchedConstruct<?, ?>)currentNode;
			
			// Mark start/end nodes
			castedNode.getStartNode().setExecutionGuaranteed(executionGuaranteed);
			castedNode.getEndNode().setExecutionGuaranteed(executionGuaranteed);
			
			for (Graph g : castedNode.getBranches())
				if (g.getNodes().size() > 0)
					markExecutionGuarantee(g.getStartNode(), g, newVal, visitedNodes);
		} else if (currentNode instanceof LoopConstruct) {
			LoopConstruct castedNode = (LoopConstruct)currentNode;
			
			// Mark conditional node
			castedNode.getConditionNode().setExecutionGuaranteed(executionGuaranteed);

			// If the condition is evaluated before the loop branch, execution of the loop branch is not guaranteed
			if (castedNode.isEvaluateConditionBefore())
				markExecutionGuarantee(castedNode.getInnerGraph().getStartNode(), castedNode.getInnerGraph(), false, visitedNodes);
			else // When the condition is evaluated after the loop branch, the branch is executed at least one time, so depends on the surrounding constructs
				markExecutionGuarantee(castedNode.getInnerGraph().getStartNode(), castedNode.getInnerGraph(), executionGuaranteed, visitedNodes);
		}
		
		// Follow outgoing edges
		for (Edge e : graph.getOutgoingEdges(currentNode, EdgeType.Control))
			markExecutionGuarantee(e.getEnd(), graph, executionGuaranteed, visitedNodes);
	}
}
